import {MCTSGameState } from "./MCTSGameState";

export enum LevelType {
    Player,
    Computer
}

export class MCTSNode {
    nSimulationTimes = 0;
    nWinTimes = 0;
    UCB = 0;
    readonly levelType: LevelType; // 只读字段只能在声明时或构造函数内赋值，但如果是对象，仍然能够调用它本身提供的方法
    gameState: MCTSGameState; // 当前节点对应的游戏状态

    readonly parent: MCTSNode | null;
    children: MCTSNode[] = [];

    constructor(levelType: LevelType, gameState: MCTSGameState, parent: MCTSNode|null) {
        this.levelType = levelType;
        this.parent = parent;
        this.gameState = gameState;
    }

    updateStatus(won: boolean) {
        this.nSimulationTimes++;
        if (won) {
            if (this.levelType === LevelType.Player) this.nWinTimes++;
            else this.nWinTimes--;
        }

        if (this.parent!!= null) // 根节点无需记录 UCB
        {
            this.UCB = this.nWinTimes * 1.0 / this.nSimulationTimes +
                1.98*Math.sqrt(Math.log(this.parent.nSimulationTimes) / this.nSimulationTimes);
        }
    }

    selectChildWithMaxUCB(): MCTSNode | null {
        if (this.children.length === 0) return null;
        let childWithMaxUCB = this.children[0];
        for (const child of this.children) {
            if (child.UCB > childWithMaxUCB.UCB) childWithMaxUCB = child;
        }

        return childWithMaxUCB;
    }

    existsUnexpandedChild(): boolean {
        return this.gameState.existsUnexpandedChild(this.getChildrenGameStates());
    }

    getChildrenGameStates(): MCTSGameState[] {
        const childrenStates:MCTSGameState[]  = [];
        for (const child of this.children) {
            childrenStates.push(child.gameState);
        }

        return childrenStates;
    }

    // ========================================== 工具函数或读取器 ==========================================
    appendChild(child: MCTSNode) {
        this.children.push(child);
    }

    getLevelType(): LevelType {
        return this.levelType;
    }

    isLeaf(): boolean {
        return this.children.length === 0;
    }

    // ========================================== 闲置函数 ==========================================
    expand() {
        if (!this.existsUnexpandedChild()) return;
        const stateOfChild = this.gameState.expand(this.getChildrenGameStates());
        const typeOfChild = this.levelType === LevelType.Computer? LevelType.Player : LevelType.Computer;
        const child = new MCTSNode(typeOfChild, stateOfChild, this);
        this.children.push(child);
    }
}